﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Engine.Sgml.Allocation {
    internal sealed class ElementsIterator : INodeIterator {
        private SElement firstAncestor;
        private SElement currentAncestor;
        private SElement current;
        private SElement root;
        private int currentDepth;

        public SElement Current {
            get { return current; }
            }

        public bool MoveNext() {
            if (current == null) {
                currentDepth = root.Depth;
                currentAncestor = SelectNextAncestor(root, root);
                if (currentAncestor == null)
                    return false;
                firstAncestor = currentAncestor;
                currentDepth = currentAncestor.Depth;
                current = currentAncestor.FirstNonCommentNode();
                return true;
                }
            SElement next = current.NextNonCommentNode();
            if (next == null) {
                SElement nextAncestor = SelectNextAncestor(currentAncestor);
                if (nextAncestor == null) {
                    currentDepth++;
                    nextAncestor = SelectNextAncestor(firstAncestor, firstAncestor.LastNonCommentNode());
                    if (nextAncestor == null) {
                        current = null;
                        return false;
                        }
                    else
                        firstAncestor = nextAncestor;
                    }
                currentAncestor = nextAncestor;
                next = currentAncestor.FirstNonCommentNode();
                }
            current = next;
            return true;
            }

        private SElement SelectNextAncestor(SElement ancestor) {
            return SelectNextAncestor(ancestor, ancestor.NextNonCommentNode());
            }

        private SElement SelectNextAncestor(SElement ancestor, SElement next) {
            while (next != null && next.FirstNonCommentNode() == null) {
                next = next.NextNonCommentNode();
                }
            if (next == null) {
                if (ancestor.Parent == null || object.ReferenceEquals(ancestor.Parent, root))
                    return null;
                else
                    return SelectNextAncestor(ancestor.Parent);
                }
            else if (currentDepth == next.Depth)
                return next;
            else
                return SelectNextAncestor(next, next.FirstNonCommentNode());
            }

        public bool MovePrevious() {
            if (current == null) {
                currentDepth = root.Depth;
                currentAncestor = SelectPrevAncestor(root, root);
                if (currentAncestor == null)
                    return false;
                firstAncestor = currentAncestor;
                currentDepth = currentAncestor.Depth;
                current = currentAncestor.LastNonCommentNode();
                return true;
                }
            SElement next = current.PreviousNonCommentNode();
            if (next == null) {
                SElement prevAncestor = SelectPrevAncestor(currentAncestor);
                if (prevAncestor == null) {
                    currentDepth++;
                    prevAncestor = SelectPrevAncestor(firstAncestor, firstAncestor.LastNonCommentNode());
                    if (prevAncestor == null) {
                        current = null;
                        return false;
                        }
                    else
                        firstAncestor = prevAncestor;
                    }
                currentAncestor = prevAncestor;
                next = currentAncestor.LastNonCommentNode();
                }
            current = next;
            return true;
            }

        private SElement SelectPrevAncestor(SElement ancestor) {
            return SelectPrevAncestor(ancestor, ancestor.PreviousNonCommentNode());
            }

        private SElement SelectPrevAncestor(SElement ancestor, SElement next) {
            while (next != null && next.LastNonCommentNode() == null) {
                next = next.PreviousNonCommentNode();
                }
            if (next == null) {
                if (ancestor.Parent == null || object.ReferenceEquals(ancestor.Parent, root))
                    return null;
                else
                    return SelectPrevAncestor(ancestor.Parent);
                }
            else if (currentDepth == next.Depth)
                return next;
            else
                return SelectPrevAncestor(next, next.LastNonCommentNode());
            }

        public void Reset(SElement element) {
            Guard.ArgumentNotNull(element, "element");
            root = element;
            current = null;
            }
        }
    }
